package net.sf.midollo.net.httptunnel.server;

import net.sf.midollo.io.DefaultIOStream;
import net.sf.midollo.io.IOStream;
import net.sf.midollo.net.protocol.ProtocolFactory;
import net.sf.midollo.net.protocol.ProtocolRunner;

import java.io.IOException;
import java.io.InputStream;
import java.io.InterruptedIOException;
import java.io.OutputStream;

public class HttpTunnelServerSession {

	protected final Object readerLock = new Object();
	protected final HttpTunnelServer parent;
	protected final String sessionId;
	protected final ProtocolFactory protocolFactory;
	protected IOStream ioRequest;
	protected InputStream currentInput;
	protected int contentLength;

	public HttpTunnelServerSession(HttpTunnelServer parent, String sessionId, ProtocolFactory protocolFactory) throws IOException {
		this.parent = parent;
		this.sessionId = sessionId;
		this.protocolFactory = protocolFactory;
		this.currentInput = null;
		this.contentLength = 0;
	}

	public String getSessionId() {
		return this.sessionId;
	}

	public void writeLoop(OutputStream os) throws IOException {
		try {
			InputStream is = new HttpTunnelServerInputStream(this);
			this.ioRequest = new DefaultIOStream(is, os);
			ProtocolRunner protocolRunner = new ProtocolRunner(ioRequest, this.protocolFactory);
			protocolRunner.runProtocol();
		} finally {
			this.parent.removeSession(this);
		}
	}

	public void readLoop(InputStream is, int contentLength) throws IOException {
		synchronized (this.readerLock) {
			this.currentInput = is;
			this.contentLength = contentLength;
			this.readerLock.notifyAll();
			try {
				this.readerLock.wait();
			} catch (InterruptedException e) {
				throw new InterruptedIOException(e.toString());
			}
		}
	}

	public int requestRead(byte[] b, int off, int len) throws IOException {
		synchronized (this.readerLock) {
			if (this.contentLength <= 0) {
				try {
					this.readerLock.wait();
				} catch (InterruptedException e) {
					return 0;
				}
			}
		}
		if (len > this.contentLength)
			len = this.contentLength;
		int nread = 0;
		try {
			nread = this.currentInput.read(b, off, len);
			if (nread < 0)
				throw new IOException();
		} catch (InterruptedIOException e) {
			nread = e.bytesTransferred;
		} finally {
			synchronized (this.readerLock) {
				if (nread < 0)
					this.contentLength = 0;
				else
					this.contentLength -= nread;
				if (this.contentLength <= 0)
					this.readerLock.notifyAll();
			}
		}
		return nread;
	}

	public int requestAvailable() throws IOException {
		synchronized (this.readerLock) {
			if (this.contentLength > 0)
				return this.contentLength;
		}
		return 0;
	}
}
